﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using StyleMVVM.Data;

namespace StyleMVVM.Validation.Impl
{
	internal class AttributeValidationRule : ISyncValidationRule
	{
		private IReflectionService reflectionService;
		private readonly string propertyName;
		private readonly ValidationAttribute attribute;
		private readonly GetPropertyDelegate propertyDelegate;
		private readonly ValidationState errorState = ValidationState.Invalid;

		public AttributeValidationRule(ValidationAttribute attribute,
		                               IReflectionService reflectionService,
		                               Type objectType,
		                               string propertyName)
		{
			this.reflectionService = reflectionService;
			this.propertyName = propertyName;
			this.attribute = attribute;

			Name = string.Format("{0}.{1}", propertyName, attribute.GetType().Name);

			Namespace = "Attribute|" + objectType.FullName;

			propertyDelegate = reflectionService.CreateGetPropertyDelegate(objectType, propertyName, null);

			if (attribute is RequiredAttribute)
			{
				errorState = ValidationState.InvalidRequired;
			}
		}

		public string Namespace { get; private set; }

		public string DisplayName { get; private set; }

		public string Name { get; private set; }

		public IEnumerable<string> DependentProperties()
		{
			List<string> returnValue = new List<string>();

			IValidationDependentAttribute dependentAttribute = attribute as IValidationDependentAttribute;

			if (dependentAttribute != null)
			{
				IEnumerable<string> properties = dependentAttribute.DependentProperties();

				if (properties != null)
				{
					returnValue.AddRange(properties);
				}

				if (!returnValue.Contains(propertyName))
				{
					returnValue.Add(propertyName);
				}
			}
			else
			{
				returnValue.Add(propertyName);
			}

			return returnValue;
		}

		public IEnumerable<string> Standards()
		{
			return new List<string> { "Default" };
		}

		public void Validate(IRuleExecutionContext context)
		{
			System.ComponentModel.DataAnnotations.ValidationContext validationContext =
				new System.ComponentModel.DataAnnotations.ValidationContext(context.ValidationObject) { MemberName = propertyName };

			object propertyValue = propertyDelegate(context.ValidationObject, null, false);

			ValidationResult results =
				attribute.GetValidationResult(propertyValue, validationContext);

			if (results != null)
			{
				context.State = errorState;
				context.Message = results.ErrorMessage;

				if (results.MemberNames != null)
				{
					foreach (string memberName in results.MemberNames)
					{
						if (errorState == ValidationState.Invalid)
						{
							context.AddError(memberName, results.ErrorMessage);
						}
						else if (errorState == ValidationState.InvalidRequired)
						{
							context.AddRequired(memberName, results.ErrorMessage);
						}
					}
				}
			}
		}
	}
}